package thu.ccf2013.wiki;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import thuai.ccf2013.util.MyFileReader;
import thuai.ccf2013.util.MyStrUtil;

/**
 * This class recognizes the infobox of Baidu Baike. The recognizing process is
 * similar with recognizing queries in Query.java.
 * 
 * @author WangYan
 * 
 */
public class Infobox {

	private static Infobox NIL = null;

	private Infobox() {

	}

	public static Infobox getNil() {
		if (NIL == null) {
			NIL = new Infobox();
			NIL.anchorText = "NIL";
			NIL.name = "NIL";
			NIL.id = "NIL";
			NIL.category = "";
			NIL.facts = new HashMap<String, String>();
		}
		return NIL;
	}

	private Map<String, String> facts = null;

	public Map<String, String> getFacts() {
		return facts;
	}

	private String id = null;

	public String getId() {
		return id;
	}

	public String getName() {
		return name;
	}

	public String getCategory() {
		return category;
	}

	private String name = null;
	private String category = null;
	private String anchorText = null;
	private List<String> page = null;

	public List<String> getPage() {
		return page;
	}

	public String getAnchorText() {
		return anchorText;
	}

	private static boolean patternInitialized = false;

	private static Pattern patWikiId = null;
	private static Pattern patCate = null;
	private static Pattern patName = null;
	private static Pattern patFactBegin = null;
	private static Pattern patFactContent = null;
	private static Pattern patFactEnd = null;
	private static Pattern patPageBegin = null;
	private static Pattern patPageEnd = null;

	private static void initPattern() {
		patWikiId = Pattern.compile("<entity_id>(.*)<");
		patCate = Pattern.compile("<category>(.*)<");
		patName = Pattern.compile("<name>(.*)<");
		patFactBegin = Pattern.compile("<fact>");
		patFactEnd = Pattern.compile("</fact>");
		patFactContent = Pattern.compile("<(.*)>(.*)</(.*)>");
		patPageBegin = Pattern.compile("<link_page>");
		patPageEnd = Pattern.compile("</link_page>");
	}

	public Infobox(List<String> strList) {

		if (!patternInitialized) {
			initPattern();
		}

		facts = new HashMap<String, String>();
		page = new ArrayList<String>();
		
		boolean isFact = false;
		boolean isPage = false;

		for (String line : strList) {
			Matcher matcher = null;
			if (isFact) {
				matcher = patFactContent.matcher(line);
				if (matcher.find()) {
					String tag = matcher.group(1);
					String content = matcher.group(2);
					assert (tag.equals(matcher.group(3)));

					facts.put(tag.toLowerCase(), content.toLowerCase());
					continue;
				}
				matcher = patFactEnd.matcher(line);
				if (matcher.find()) {
					isFact = false;
					continue;
				}

			} else if (isPage) {
				
				matcher = patPageEnd.matcher(line);
				if (matcher.find()) {
					isPage = false;
					continue;
				} else {
					page.add(line);
				}
				
			} else {
				matcher = patWikiId.matcher(line);
				if (matcher.find()) {
					this.id = matcher.group(1);
					continue;
				}
				matcher = patCate.matcher(line);
				if (matcher.find()) {
					this.category = matcher.group(1).toLowerCase();
					continue;
				}
				matcher = patName.matcher(line);
				if (matcher.find()) {
					this.name = matcher.group(1).toLowerCase();
					continue;
				}
				matcher = patFactBegin.matcher(line);
				if (matcher.find()) {
					isFact = true;
					continue;
				}
				matcher = patPageBegin.matcher(line);
				if (matcher.find()) {
					isPage = true;
					continue;
				}
			}
		}
		int last = category.lastIndexOf("\\");
		int dot = category.lastIndexOf(".");
		if (last >= 0 && last < dot) {
			anchorText = category.substring(last + 1, dot);
		} else {
			anchorText = "";
		}
//		System.out.println(page.size());
	}

	public String toString() {
		StringBuilder sb = new StringBuilder();
		sb.append(id + "\t" + name + "\t" + category + "\n");
		for (String tag : facts.keySet()) {
			// sb.append(tag + ": " + facts.get(tag) + "\n");
			sb.append(tag + ",");
		}
		return sb.toString();
	}

	public static Set<Infobox> loadFromFile(String file) {
		Set<Infobox> set = new HashSet<Infobox>();
		List<String> buffer = new ArrayList<String>();
		MyFileReader reader = new MyFileReader(file);
		String temp = null;
		boolean isPart = false;
		while ((temp = reader.getNextLine()) != null) {
			temp = temp.trim();

			if (temp.contains("</entity>")) {
				set.add(new Infobox(buffer));
			}
			if (temp.contains("<entity>")) {
				buffer.clear();
			}

			buffer.add(temp);
		}

		/*
		 * check validation
		 */

		return set;
	}

	public static Map<String, Integer> countTags(Set<Infobox> list) {
		Map<String, Integer> counter = new HashMap<String, Integer>();

		for (Infobox box : list) {
			Set<String> keys = box.facts.keySet();
			for (String tag : keys) {
				if (counter.containsKey(tag)) {
					Integer c = counter.get(tag);
					counter.put(tag, c + 1);
				} else {
					counter.put(tag, 1);
				}
			}
		}
		return counter;
	}

	public static Map<Integer, List<String>> sortTagByCount(
			Map<String, Integer> map) {
		Map<Integer, List<String>> sorter = new TreeMap<Integer, List<String>>();

		for (String tag : map.keySet()) {
			Integer count = map.get(tag);
			if (sorter.containsKey(count)) {
				sorter.get(count).add(tag);
			} else {
				List<String> list = new ArrayList();
				list.add(tag);
				sorter.put(count, list);
			}
		}

		return sorter;
	}

	public static Set<Infobox> findByEntityName(Set<Infobox> list, String name) {
		Set<Infobox> candidate = new HashSet<Infobox>();

		for (Infobox box : list) {
			for (String content : box.facts.values()) {
				int dis = MyStrUtil.LevenshteinDistance(content, name);
				if (dis + name.length() == content.length()
						|| dis + content.length() == name.length()) {
					candidate.add(box);
					break;
				}
			}
		}

		return candidate;
	}

	public static Infobox findById(Set<Infobox> list, String id) {
		for (Infobox box : list) {
			if (box.id.equals(id)) {
				return box;
			}
		}
		return null;
	}

	public static Set<Infobox> washCandidate(Set<Infobox> list, String name) {
		Set<Infobox> candidate = new HashSet<Infobox>();
		int count = 0;
		for (Infobox box : list) {
			String boxName = box.getName();
			for (int i = 0; i < name.length(); i++) {
				if (boxName.contains(name.substring(i, i + 1))) {
					count++;
				}
			}
			if (count * 2 > name.length()) {
				candidate.add(box);
			}
		}
		return candidate;
	}

}
